Invoking Interface Members at the Object Level

Now that you have some classes that support the IPointy interface, the next question is how you interact with the new functionality. The most straightforward way to interact with functionality supplied by a given interface is to invoke the members directly from the object level (provided the interface members are not implemented explicitly; more details later in the section "Resolving Name Clashes via Explicit Interface Implementation"). For example, consider the following Main() method:

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Interfaces *****\n");

    // Call Points property defined by IPointy.
    Hexagon hex = new Hexagon();
    Console.WriteLine("Points: {0}", hex.Points);
    Console.ReadLine();
}

This approach works fine in this particular case, given that you are well aware that the Hexagon type has implemented the interface in question and therefore has a Points property. Other times, however, you may not be able to determine which interfaces are supported by a given type. For example, suppose you have an array containing 50 Shape-compatible types, only some of which support IPointy. Obviously, if you attempt to invoke the Points property on a type that has not implemented IPointy, you receive an error. So how can you dynamically determine if a class or structure supports the correct interface?

One way to determine at runtime whether a type supports a specific interface is to make use of an explicit cast. If the type does not support the requested interface, you receive an InvalidCastException. To handle this possibility gracefully, use structured exception handling as in the following example:

static void Main(string[] args)
{
...
    // Catch a possible InvalidCastException.
    Circle c = new Circle("Lisa");
    IPointy itfPt = null;
    try
    {
        itfPt = (IPointy)c;
        Console.WriteLine(itfPt.Points);
    }
    catch (InvalidCastException e)
    {
        Console.WriteLine(e.Message);
    }
    Console.ReadLine();
}

While you could use try/catch logic and hope for the best, it would be ideal to determine which interfaces are supported before invoking the interface members in the first place. Let’s see two ways of doing so.

Obtaining Interface References: The as Keyword

You can determine whether a given type supports an interface by using the as keyword, introduced in Chapter 6. If the object can be treated as the specified interface, you are returned a reference to the interface in question. If not, you receive a null reference. Therefore, be sure to check against a null value before proceeding:

static void Main(string[] args)
{
...
    // Can we treat hex2 as IPointy?
    Hexagon hex2 = new Hexagon("Peter");
    IPointy itfPt2 = hex2 as IPointy;

    if(itfPt2 != null)
        Console.WriteLine("Points: {0}", itfPt2.Points);
    else
        Console.WriteLine("OOPS! Not pointy...");
    Console.ReadLine();
}

Notice that when you use the as keyword, you have no need to use try/catch logic, given that if the reference is not null, you know you are calling on a valid interface reference.

Obtaining Interface References: The is Keyword

You may also check for an implemented interface using the is keyword (also first discussed in Chapter 6). If the object in question is not compatible with the specified interface, you are returned the value false. On the other hand, if the type is compatible with the interface in question, you can safely call the members without needing to use try/catch logic.

To illustrate, assume we have an array of Shape types containing some members that implement IPointy. Notice how we are able to determine which item in the array supports this interface using the is keyword, as shown in this retrofitted Main() method:

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Interfaces *****\n");

    // Make an array of Shapes.
    Shape[] myShapes = { new Hexagon(), new Circle(),
        new Triangle("Joe"), new Circle("JoJo")} ;

    for(int i = 0; i < myShapes.Length; i++)
    {
        // Recall the Shape base class defines an abstract Draw()
        // member, so all shapes know how to draw themselves.
        myShapes[i].Draw();

        // Who's pointy?
        if(myShapes[i] is IPointy)
            Console.WriteLine("-> Points: {0}", ((IPointy) myShapes[i]).Points);
        else
            Console.WriteLine("-> {0}\'s not pointy!", myShapes[i].PetName);
        Console.WriteLine();
    }
    Console.ReadLine();
}

The output is as follows:

***** Fun with Interfaces *****

Drawing NoName the Hexagon
-> Points: 6

Drawing NoName the Circle
-> NoName's not pointy!

Drawing Joe the Triangle
-> Points: 3

Drawing JoJo the Circle
-> JoJo's not pointy!